﻿namespace Codeblack.TfsScrumboard.Services
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using Codeblack.TfsScrumboard.Entities;
    using Codeblack.TfsScrumboard.Extensions;
    using Codeblack.TfsScrumboard.Interfaces;
    using Codeblack.TfsScrumboard.Resources;
    using Codeblack.TfsScrumboard.ViewModels;
    using Microsoft.TeamFoundation.WebAccess;
    using Microsoft.TeamFoundation.WebAccess.Common;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    public class ScrumboardService : IScrumboardService
    {
        // NOTE: several ways to get sprints and the workitems within them:
        //       - use a workitem type to define each sprint (as is currently assumed)
        //       - use the iteration-path and assume (or let the user provide) how the structure is used (e.g. 1st level = release, 2nd level = sprint)
        //       - provide specific queries for the product backlog and each sprint backlog

        #region Fields

        private ITeamFoundationRepository _teamFoundationRepository = null;

        #endregion

        #region Constructor

        public ScrumboardService(ITeamFoundationRepository teamFoundationRepository)
        {
            _teamFoundationRepository = teamFoundationRepository;
        }

        #endregion

        #region IScrumboardService implementation

        #region Backlogs

        public BacklogsViewModel GetBacklogs(Project project)
        {
            List<Backlog> projectBacklogs = new List<Backlog>();
            List<Backlog> historyBacklogs = new List<Backlog>();
            List<Backlog> currentBacklogs = new List<Backlog>();
            List<Backlog> futureBacklogs = new List<Backlog>();
            List<Backlog> unscheduledBacklogs = new List<Backlog>();

            DateTime now = DateTime.Now;

            foreach (Backlog backlog in this._teamFoundationRepository.GetBacklogs(project))
            {
                if (backlog is ProductBacklog)
                {
                    projectBacklogs.Add(backlog);
                }
                else
                {
                    if (!backlog.StartDate.HasValue)
                    {
                        unscheduledBacklogs.Add(backlog);
                    }
                    else if (backlog.FinishDate.HasValue && backlog.FinishDate.Value < now)
                    {
                        historyBacklogs.Add(backlog);
                    }
                    else if (backlog.FinishDate.HasValue && backlog.StartDate.Value <= now && backlog.FinishDate.Value >= now)
                    {
                        currentBacklogs.Add(backlog);
                    }
                    else if (backlog.StartDate.Value > now)
                    {
                        futureBacklogs.Add(backlog);
                    }
                }
            }

            futureBacklogs.AddRange(unscheduledBacklogs.OrderBy(b => b.Name));

            return new BacklogsViewModel(projectBacklogs.ToViewModels(),
                                         historyBacklogs.ToViewModels(),
                                         currentBacklogs.ToViewModels(),
                                         futureBacklogs.ToViewModels());
        }

        #endregion

        #region Planning

        public PlanningViewModel GetPlanning(Project project, string backlogName)
        {
            if (string.IsNullOrEmpty(backlogName))
            {
                // TODO: get (first) current backlog as the default backlog
                backlogName = ScrumboardResources.ProductBacklogName;
            }

            backlogName = backlogName.Trim();
            string iterationPath = backlogName == ScrumboardResources.ProductBacklogName ? project.Name : project.GetFullIterationPath(backlogName);

            Workitem[] workitems = this._teamFoundationRepository.GetWorkitems(project, iterationPath);

            return new PlanningViewModel()
            {
                BacklogName = backlogName,
                QueryResults = new QueryResultsViewModel()
                {
                    TotalWorkItems = workitems.Length,
                    TotalStoryPoints = workitems.Length > 0 ? (from w in workitems select w.StoryPoints).Aggregate((totalStoryPoints, storyPoints) => totalStoryPoints += storyPoints) : 0.0
                },
                WorkItems = workitems.ToViewModels()
            };
        }

        public PlanningViewModel UpdatePriority(Project project, string backlogName, int id, int? previousId, int? nextId)
        {
            PlanningViewModel model = null;

            backlogName = backlogName.Trim();
            string iterationPath = backlogName == ScrumboardResources.ProductBacklogName ? project.Name : project.GetFullIterationPath(backlogName);

            Workitem workitem = this._teamFoundationRepository.GetWorkitem(project, id);
            if (workitem != null)
            {
                // retrieve all work-items in the backlog of the work-item
                List<Workitem> workitems = new List<Workitem>(this._teamFoundationRepository.GetWorkitems(project, iterationPath));

                // find the work-item to move
                Workitem movingWorkitem = (from w in workitems where w.ID == id select w).FirstOrDefault();
                if (movingWorkitem != null)
                {
                    Workitem previousWorkitem = null;
                    if (previousId.HasValue)
                    {
                        previousWorkitem = (from w in workitems where w.ID == previousId.Value select w).FirstOrDefault();
                    }

                    workitems.Remove(movingWorkitem);

                    if (previousWorkitem != null)
                    {
                        movingWorkitem.Priority = previousWorkitem.Priority + 1;

                        int index = workitems.IndexOf(previousWorkitem);
                        workitems.Insert(index + 1, movingWorkitem);
                    }
                    else
                    {
                        movingWorkitem.Priority = 1;

                        workitems.Insert(0, movingWorkitem);
                    }

                    // change priority of each work-item if needed
                    Workitem previous = null;
                    foreach (Workitem current in workitems)
                    {
                        if (previous != null && current.Priority <= previous.Priority)
                        {
                            current.Priority = previous.Priority + 1;
                        }

                        previous = current;
                    }

                    // save the changed work-items
                    foreach (Workitem current in workitems)
                    {
                        current.Save(null);
                    }

                    model = this.GetPlanning(project, backlogName);
                }
            }

            return model;
        }

        public PlanningViewModel UpdatePlanning(Project project, string backlogName, int id, string updateBacklogName)
        {
            PlanningViewModel model = null;

            backlogName = backlogName.Trim();
            //string iterationPath = backlogName == ScrumboardResources.ProductBacklogName ? project.Name : project.GetFullIterationPath(backlogName);

            updateBacklogName = updateBacklogName.Trim();
            string updateIterationPath = updateBacklogName == ScrumboardResources.ProductBacklogName ? project.Name : project.GetFullIterationPath(updateBacklogName);

            Workitem workitem = this._teamFoundationRepository.GetWorkitem(project, id);
            if (workitem != null)
            {
                workitem.IterationPath = updateIterationPath;

                workitem.Save(null);

                model = this.GetPlanning(project, backlogName);
            }

            return model;
        }

        #endregion

        //WorkItemsViewModel IScrumboardService.GetWorkItems(Project project, string backlogName)
        //{
        //    if (string.IsNullOrEmpty(backlogName))
        //    {
        //        backlogName = ScrumboardResources.ProductBacklogName;
        //    }

        //    Dictionary<string, string> queryValues = new Dictionary<string, string>();
        //    queryValues.Add(TeamProjectKey, project.Name);
        //    queryValues.Add(SprintTypeKey, SprintType);
        //    queryValues.Add(IterationPathKey, backlogName == ScrumboardResources.ProductBacklogName ? project.Name : project.GetFullIterationPath(backlogName));

        //    string queryFormat = string.Format(CultureInfo.InvariantCulture,
        //                                       "{0}{1}{2}",
        //                                       WorkItemsQuery,
        //                                       backlogName == ScrumboardResources.ProductBacklogName ? ProductBacklogWhere : SprintBacklogWhere,
        //                                       WorkItemsOrderBy);

        //    string queryString = string.Format(CultureInfo.InvariantCulture, queryFormat, WorkItemFieldNames, WorkItemTypes, PriorityFieldName);

        //    List<Models.WorkItem> workItems = new List<Models.WorkItem>();

        //    foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem in project.Store.Query(queryString, queryValues))
        //    {
        //        workItems.Add(new Models.WorkItem()
        //        {
        //            ID = workItem.GetFieldValue<int>(IDFieldName),
        //            Title = workItem.GetFieldValue<string>(TitleFieldName),
        //            //StoryPoints = workItem.GetFieldValue<double>(StoryPointsFieldName),
        //            StoryPoints = 0,
        //            Status = workItem.GetFieldValue<string>(StatusFieldName),
        //            IterationPath = workItem.GetFieldValue<string>(IterationPathFieldName),
        //            AssignedTo = workItem.GetFieldValue<string>(AssignedToFieldName),
        //            WorkItemType = workItem.GetFieldValue<string>(WorkItemTypeFieldName)
        //        });
        //    }

        //    return new WorkItemsViewModel()
        //    {
        //        BacklogName = backlogName,
        //        WorkItems = workItems.ToArray()
        //    };
        //}

        #endregion
    }
}
